package kv

import (
	"context"
	"errors"
	"github.com/go-redis/redis/extra/redisotel"
	"github.com/go-redis/redis/v8"
	"net"
	"time"
)

type Config struct {
	Addrs       []string `toml:"addrs" json:"addrs"`
	Pwd         string   `toml:"pwd" json:"pwd"`
	PoolSize    int      `toml:"pool_size" json:"pool_size"`
	ReadTimeout int      `toml:"read_timeout" json:"read_timeout"` //单位ms
}

func (c *Config) Parse() (*redis.Options, error) {
	redisNum := len(c.Addrs)
	if redisNum < 1 {
		return nil, errors.New("redis addrs is empty")
	}
	ch := make(chan []string, redisNum)
	for i := 0; i < redisNum; i++ {
		list := make([]string, redisNum)
		for j := 0; j < redisNum; j++ {
			list[j] = c.Addrs[(i+j)%redisNum]
		}
		ch <- list
	}

	if c.ReadTimeout < 100 {
		c.ReadTimeout = 100
	}

	options := &redis.Options{
		Password:     c.Pwd,
		PoolSize:     c.PoolSize,
		ReadTimeout:  time.Duration(c.ReadTimeout) * time.Millisecond,
		WriteTimeout: time.Second,
		PoolTimeout:  100 * time.Millisecond,
		IdleTimeout:  600 * time.Second,
		Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
			list := <-ch
			ch <- list
			for _, addr := range list {
				c, err := net.DialTimeout("tcp", addr, 100*time.Millisecond)
				if err == nil {
					return c, nil
				}
			}
			return nil, errors.New("all redis down")
		},
	}
	return options, nil
}

type Client struct {
	*redis.Client
}

func New(c *Config) *Client {
	options, err := c.Parse()
	if err != nil {
		panic(err)
	}
	client := &Client{
		redis.NewClient(options),
	}
	client.AddHook(redisotel.TracingHook{})
	return client
}
